package com.ophiux.utils.wechat;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ophiux.utils.common.StringUtil;
/**
 * 2018-09-29 陈辉新增
 * 自定义xml解析
 * @author chenh
 *
 */
public class MyXmlParser {

	/**
	 * x自定义xml解析
	 * @param xml
	 * @param xxe true:防护    false：不防护
	 * @return
	 */
	public static List<MyNode> parse(String xml , boolean xxe) throws Exception{
		
		if(StringUtil.isEmpty(xml)) return null;
		
		//xml  xxe攻击过滤  若开启xxe防护
		if(xxe && !XXE_Validate(xml)){
			throw new Exception("出现了XXE攻击，xml："+xml);
		}
		
		//获取xml节点列表
		List<MyNode> nodes = null;
		try {
			 String[] labels = getXMLNodeNames(xml);
			 nodes = convert(xml,labels);
			 
		} catch (Exception e) {
			e.printStackTrace();
		}
		return nodes;
	}
	
	public static List<MyNode> convert(String xml , String[] labels) throws Exception{
		
		List<MyNode> nodes = new ArrayList<MyNode>();
		
		if(labels == null || labels.length == 0){
			return null;
		}
		//开始转换，暂时不考虑节点重复
		for(String label : labels){
			//构造正则
			String regx = "<"+label+">([\\s\\S]*?)</"+label+">";	//截取xml节点内容
			Pattern p = Pattern.compile(regx);//正则表达式 commend by danielinbiti
		    Matcher m = p.matcher(xml);//
		    while (m.find()) {
		    	String value = m.group(1);
		    	//判断是否包含CDATA
		    	if(value.contains("<![CDATA["))
		    		value = convertCDATA(value);
		    	
		    	MyNode node = new MyNode(label, value);
		    	nodes.add(node);
		    }
		}
		return nodes;
	}
	
	
	public static String convertCDATA(String con)throws Exception{
		
		String value = con;
		
		if(value == null) return null;
		
		/*String regx = "<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>";	//截取CDATA内容
		//"<"+label+">([\\s\\S]*?)</"+label+">";
		 
		Pattern p = Pattern.compile(regx);//正则表达式 commend by danielinbiti
	    Matcher m = p.matcher(con);//
	    while (m.find()) {
	    	value = m.group(1);
	    }*/
		try {
			value = value.replaceAll("<!\\[CDATA\\[", "");
			value = value.replaceAll("\\]\\]>", "");
		} catch (Exception e) {
			e.printStackTrace();
			return con;
		}
		
		
	    return value;
	}
	
	/**
	 * 获取xml节点列表
	 * @throws Exception 
	 */
	public static String[] getXMLNodeNames(String xml) throws Exception{
		
		ArrayList<String> resultList = new ArrayList<String>();
		
		try {
			String regex = "</([\\s\\S]*?)>";	//截取xml节点名称
			
			Pattern pattern = Pattern.compile(regex);
			Matcher m = pattern.matcher(xml);   
			
			while (m.find()) {
			  resultList.add(m.group(1));//
			}
		} catch (Exception e) {
			throw e;
		}
		
		return resultList.toArray(new String[resultList.size()]);
	}
	
	public static List getContext(String html , String label) {
	    List resultList = new ArrayList();
	    
	    
	  //  String regx = "<"+label+">([^</]+)</"+label+">";
	//    String regx = "<"+label+">([\\s\\S]*?)</"+label+">";	//截取xml节点内容
	    
	  //  String regx = "</([\\s\\S]*?)>";	//截取xml节点名称
	    
	 //   String regx = "<![CDATA[([\\s\\S]*?)]]>";	//截取CDATA内容
	    
	    //">([^</]+)</"
	    
	    String regx = "<"+label+">([\\s\\S]*?)</"+label+">";
	    
	    Pattern p = Pattern.compile(regx);//正则表达式 commend by danielinbiti
	    Matcher m = p.matcher(html );//
	    while (m.find()) {
	      resultList.add(m.group(1));//
	    }
	    return resultList;
	}
	/**
	* @param args
	 * @throws Exception 
	*/
	public static void ccd11(String[] args) {
	 	String xml ="<xml><appid><![CDATA[wxb50d6933834082a6]]></appid><attach><![CDATA[-1]]></attach><bank_type><![CDATA[CCB_DEBIT]]></bank_type><cash_fee><![CDATA[1]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[N]]></is_subscribe><mch_id><![CDATA[1327201101]]></mch_id><nonce_str><![CDATA[1343444521]]></nonce_str><openid><![CDATA[o72wcxFzhsMo4bUcqSHSqnn3Qx38]]></openid><out_trade_no><![CDATA[ZJJ201711101603028374224]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[D2369C46B7982D4D63DA9A816EE0EBB1]]></sign><sub_appid><![CDATA[wx89593e140fa7b55c]]></sub_appid><sub_is_subscribe><![CDATA[N]]></sub_is_subscribe><sub_mch_id><![CDATA[1431564602]]></sub_mch_id><sub_openid><![CDATA[oNcL8w1_fyn2Ef07pPt6bHYjKQh0]]></sub_openid><time_end><![CDATA[20171202134351]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[4200000028201712028510159567]]></transaction_id></xml>";// request.getParameter("name");
	 	/*xml = xml.replace("<xml>", "")
	 			.replace("</xml>", "");*/
	    List list = getContext(xml,"appid");
	//    System.out.println(list);
	    
	   /* String cxml = "<![CDATA[wx2421b1c4370ec43b]]>";
	    String value = "无";
		try {
			value = convertCDATA(cxml);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    System.out.println(value);*/
	    
	    List<MyNode> nodes = null;
		try {
			nodes = parse(xml,true);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	    
	   /* for(MyNode node : nodes){
	    	 System.out.println(node.getName()+":"+node.getValue());
	    }*/
	    
	    
	    System.out.println("/****以下是xml解析*****/");
	    HashMap<String,String> map = null;
	   try {
		   map = (HashMap<String, String>) WXServiceUtils.doXMLParse(xml);
		   
		 /*  for(String key : map.keySet()){
			   System.out.println(key+":"+map.get(key));
		   }*/
		   
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	   
	   //两种解析结果比对
	   for(String key : map.keySet()){
		   String value = map.get(key);
		   boolean flag = false;
		   for(MyNode node : nodes){
	    	 if(value.equals(node.getValue())){
	    		 flag = true;
	    	 }
		    }
		   if(flag){
			   System.out.println(key+":"+"\t值："+value+"\t比对："+"true");
		   }else{
			   System.out.println(key+":"+"\t值："+value+"\t比对："+"false");
		   }
		   
	   }
	   
	}
	/**
	 * xml转map  
	 * 备注：2.4.2版本及以后存在
	 * 重要：仅用于 微信通知的处理,不能含"<xml>"的节点,其他不适用
	 * 2018-10-11 陈辉
	 * @param xxe true:防护    false：不防护
	 * @return
	 * @throws Exception 
	 */
	public static Map parseToMap(String xml , boolean xxe) throws Exception{

		Map map = new HashMap();
		
		List<MyNode> nodes = MyXmlParser.parse(xml , xxe);
		
		//转为map
		if(nodes == null || nodes.size() == 0){
			return null;
		}
		for(MyNode node : nodes){
			String name = node.getName();
			String value = node.getValue();
			//过滤xml节点
			if(name.equals("xml")) continue;
				map.put(name , value);
		}
		return map;
	}
	
	/**
	 * xml  xxe攻击过滤
	 * 关键字：<!DOCTYPE  、      <!ENTITY	、	PUBLIC   SYSTEM
	 * @param xmlStr
	 * @return true:验证通过； false:验证未通过
	 */
	public static boolean XXE_Validate(String xmlStr) {
		
		if( xmlStr == null || xmlStr == "" || xmlStr == "null" ) {
			
			return true;
		}
		
		//2018-11-28 chenh 取消PUBLIC和SYSTEM关键词过滤
		//|| xmlStr.contains("PUBLIC") || xmlStr.contains("SYSTEM")
		if(xmlStr.contains("<!DOCTYPE")
				|| xmlStr.contains("<!ENTITY")
				|| xmlStr.contains(".dtd")
				|| xmlStr.contains(".DTD")) {
			//存在外部注入实体
			return false;
		}
		return true;
	}
	
	
	public static void main(String[] args) {
		
		String q_xml = "<xml>"
				 + "<appid><![CDATA[wxb50d6933834082a6]]></appid>  "
				 + "<attach><![CDATA[-1]]></attach>  "
				 + " <bank_type><![CDATA[ICBC_DEBIT]]></bank_type>  "
				 + " <cash_fee><![CDATA[18040]]></cash_fee>  "
				 + " <fee_type><![CDATA[CNY]]></fee_type>  "
				 + " <is_subscribe><![CDATA[N]]></is_subscribe>  "
				 + " <mch_id><![CDATA[1327201101]]></mch_id>  "
				 + " <nonce_str><![CDATA[1012081576]]></nonce_str>  "
				 + " <openid><![CDATA[o72wcxDTD4N2MBQO1oT2A37aufgI]]></openid>  "
				 + " <out_trade_no><![CDATA[9201811251011059262691]]></out_trade_no>  "
				 + " <result_code><![CDATA[SUCCESS]]></result_code>  "
				 + " <return_code><![CDATA[SUCCESS]]></return_code>  "
				 + " <sign><![CDATA[85246F465499A8EED3678F7E5C9EC9C8]]></sign>  "
				 + " <sub_appid><![CDATA[wx89593e140fa7b55c]]></sub_appid>  "
				 + " <sub_is_subscribe><![CDATA[N]]></sub_is_subscribe>  "
				 + " <sub_mch_id><![CDATA[1431564602]]></sub_mch_id>  "
				 + " <sub_openid><![CDATA[oNcL8w8g-95OteMLeCgOfmeDVF1M]]></sub_openid>  "
				 + " <time_end><![CDATA[20181125101214]]></time_end>  "
				 + " <total_fee>18040</total_fee>  "
				 + " <trade_type><![CDATA[JSAPI]]></trade_type>  "
				 + " <transaction_id><![CDATA[4200000211201811253877369013]]></transaction_id> "
			   + "</xml>";
		
	 	String strxml ="<xml><appid><![CDATA[wxb50d6933834082a6]]></appid><attach><![CDATA[-1]]></attach><bank_type><![CDATA[CCB_DEBIT]]></bank_type><cash_fee><![CDATA[1]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[N]]></is_subscribe><mch_id><![CDATA[1327201101]]></mch_id><nonce_str><![CDATA[1343444521]]></nonce_str><openid><![CDATA[o72wcxFzhsMo4bUcqSHSqnn3Qx38]]></openid><out_trade_no><![CDATA[ZJJ201711101603028374224]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[D2369C46B7982D4D63DA9A816EE0EBB1]]></sign><sub_appid><![CDATA[wx89593e140fa7b55c]]></sub_appid><sub_is_subscribe><![CDATA[N]]></sub_is_subscribe><sub_mch_id><![CDATA[1431564602]]></sub_mch_id><sub_openid><![CDATA[oNcL8w1_fyn2Ef07pPt6bHYjKQh0]]></sub_openid><time_end><![CDATA[20171202134351]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[4200000028201712028510159567]]></transaction_id></xml>";// request.getParameter("name");
	 	try {
		//	Map map = WXServiceUtils.doXMLParse(q_xml);
	 		Map map = parseToMap(q_xml , true);
			System.out.println(map);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
}
